<!DOCTYPE html>
<html>
<head><meta name="generator" content="Hexo 3.9.0">
    

    

    



    <meta charset="utf-8">
    
    
    
    <title>泛型初解 | 奔跑的蜗牛 | 虚心若愚</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    
    <meta name="theme-color" content="#3F51B5">
    
    
    <meta name="keywords" content="泛型">
    <meta name="description" content="来自疯狂Java讲义第三版，泛型章节 概念泛型:就是允许在定义类、接口、方法时使用类型形参，这个类型形参将在声明变量、创建对象、调用方法时动态地指定（即传入实际的类型参数，也可称为类型实参）。Java5中改写了集合框架中的全部接口和类，为这些接口、类增加了泛型支持，从而可以在声明集合变量、创建集合对象向时传入类型实参。例如List、ArrayList。 当创建带泛型声明的自定义类，为该类定义构造器">
<meta name="keywords" content="泛型">
<meta property="og:type" content="article">
<meta property="og:title" content="泛型初解">
<meta property="og:url" content="http://windynature.oschina.io/2017/02/11/java/泛型一/index.html">
<meta property="og:site_name" content="奔跑的蜗牛">
<meta property="og:description" content="来自疯狂Java讲义第三版，泛型章节 概念泛型:就是允许在定义类、接口、方法时使用类型形参，这个类型形参将在声明变量、创建对象、调用方法时动态地指定（即传入实际的类型参数，也可称为类型实参）。Java5中改写了集合框架中的全部接口和类，为这些接口、类增加了泛型支持，从而可以在声明集合变量、创建集合对象向时传入类型实参。例如List、ArrayList。 当创建带泛型声明的自定义类，为该类定义构造器">
<meta property="og:locale" content="zh-CN">
<meta property="og:updated_time" content="2020-03-18T04:04:03.813Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="泛型初解">
<meta name="twitter:description" content="来自疯狂Java讲义第三版，泛型章节 概念泛型:就是允许在定义类、接口、方法时使用类型形参，这个类型形参将在声明变量、创建对象、调用方法时动态地指定（即传入实际的类型参数，也可称为类型实参）。Java5中改写了集合框架中的全部接口和类，为这些接口、类增加了泛型支持，从而可以在声明集合变量、创建集合对象向时传入类型实参。例如List、ArrayList。 当创建带泛型声明的自定义类，为该类定义构造器">
    
        <link rel="alternative" href="/atom.xml" title="奔跑的蜗牛" type="application/atom+xml">
    
    <link rel="shortcut icon" href="/blog/favicon.ico">
    <link rel="stylesheet" href="//unpkg.com/hexo-theme-material-indigo@1.5.2/css/style.css">
    <script>window.lazyScripts=[]</script>
</head>

<body>
    <div id="loading" class="active"></div>

    <aside id="menu" class="hide" >
  <div class="inner flex-row-vertical">
    <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="menu-off">
        <i class="icon icon-lg icon-close"></i>
    </a>
    <div class="brand-wrap" style="background-image:url(/blog/img/timg.jpg)">
      <div class="brand">
        <a href="/blog/" class="avatar waves-effect waves-circle waves-light">
          <img src="/blog/img/user2.jpg">
        </a>
        <hgroup class="introduce">
          <h5 class="nickname">windy</h5>
          <a href="mailto:windynature@163.com" title="windynature@163.com" class="mail">windynature@163.com</a>
        </hgroup>
      </div>
    </div>
    <div class="scroll-wrap flex-col">
      <ul class="nav">
        
            <li class="waves-block waves-effect">
              <a href="/blog/"  >
                <i class="icon icon-lg icon-home"></i>
                主页
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/blog/archives"  >
                <i class="icon icon-lg icon-archives"></i>
                归档
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/blog/tags"  >
                <i class="icon icon-lg icon-tags"></i>
                标签
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/blog/categories"  >
                <i class="icon icon-lg icon-th-list"></i>
                分类
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="https://github.com/WindyNature" target="_blank" >
                <i class="icon icon-lg icon-github"></i>
                Github
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="https://git.oschina.net/windynature" target="_blank" >
                <i class="icon icon-lg icon-mayun"></i>
                码云
              </a>
            </li>
        
      </ul>
    </div>
  </div>
</aside>

    <main id="main">
        <header class="top-header" id="header">
    <div class="flex-row">
        <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light on" id="menu-toggle">
          <i class="icon icon-lg icon-navicon"></i>
        </a>
        <div class="flex-col header-title ellipsis">泛型初解</div>
        
        <div class="search-wrap" id="search-wrap">
            <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="back">
                <i class="icon icon-lg icon-chevron-left"></i>
            </a>
            <input type="text" id="key" class="search-input" autocomplete="off" placeholder="输入感兴趣的关键字">
            <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="search">
                <i class="icon icon-lg icon-search"></i>
            </a>
        </div>
        
        
        <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="menuShare">
            <i class="icon icon-lg icon-share-alt"></i>
        </a>
        
    </div>
</header>
<header class="content-header post-header">

    <div class="container fade-scale">
        <h1 class="title">泛型初解</h1>
        <h5 class="subtitle">
            
                <time datetime="2017-02-11T09:39:57.000Z" itemprop="datePublished" class="page-time">
  2017-02-11
</time>


	<ul class="article-category-list"><li class="article-category-list-item"><a class="article-category-list-link" href="/blog/categories/泛型/">泛型</a></li></ul>

            
        </h5>
    </div>

    

</header>


<div class="container body-wrap">
    
    <aside class="post-widget">
        <nav class="post-toc-wrap" id="post-toc">
            <h4>TOC</h4>
            <ol class="post-toc"><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#概念"><span class="post-toc-text">概念</span></a></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#深入泛型"><span class="post-toc-text">深入泛型</span></a><ol class="post-toc-child"><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#定义泛型接口、类"><span class="post-toc-text">定义泛型接口、类</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#从泛型类派生子类"><span class="post-toc-text">从泛型类派生子类</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#并不存在泛型类"><span class="post-toc-text">并不存在泛型类</span></a></li></ol></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#类型通配符"><span class="post-toc-text">类型通配符</span></a><ol class="post-toc-child"><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#使用类型通配符"><span class="post-toc-text">使用类型通配符</span></a><ol class="post-toc-child"><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#设定通配符的上限"><span class="post-toc-text">设定通配符的上限</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#设定类型形参的上限"><span class="post-toc-text">设定类型形参的上限</span></a></li></ol></li></ol></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#泛型方法"><span class="post-toc-text">泛型方法</span></a><ol class="post-toc-child"><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#定义泛型方法"><span class="post-toc-text">定义泛型方法</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#泛型方法和类型通配符的区别"><span class="post-toc-text">泛型方法和类型通配符的区别:</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#Java7的“菱形”语法与泛型构造器"><span class="post-toc-text">Java7的“菱形”语法与泛型构造器</span></a></li><li class="post-toc-item post-toc-level-2"><a class="post-toc-link" href="#设定通配符下限"><span class="post-toc-text">设定通配符下限</span></a></li></ol></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#Java8改进的类型推断"><span class="post-toc-text">Java8改进的类型推断</span></a></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#擦除和转换"><span class="post-toc-text">擦除和转换</span></a></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#泛型与数组"><span class="post-toc-text">泛型与数组</span></a></li></ol>
        </nav>
    </aside>
    
<article id="post-java/泛型一"
  class="post-article article-type-post fade" itemprop="blogPost">

    <div class="post-card">
        <h1 class="post-card-title">泛型初解</h1>
        <div class="post-meta">
            <time class="post-time" title="2017年02月11日 17:39" datetime="2017-02-11T09:39:57.000Z"  itemprop="datePublished">2017-02-11</time>

            
	<ul class="article-category-list"><li class="article-category-list-item"><a class="article-category-list-link" href="/blog/categories/泛型/">泛型</a></li></ul>



            
<span id="busuanzi_container_page_pv" title="文章总阅读量" style='display:none'>
    <i class="icon icon-eye icon-pr"></i><span id="busuanzi_value_page_pv"></span>
</span>


            

        </div>
        <div class="post-content" id="post-content" itemprop="postContent">
            <p>来自疯狂Java讲义第三版，泛型章节</p>
<h1 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h1><p><code>泛型</code>:就是允许在定义类、接口、方法时使用类型形参，这个类型形参将在声明变量、创建对象、调用方法时动态地指定（即传入实际的类型参数，也可称为类型实参）。<br>Java5中改写了集合框架中的全部接口和类，为这些接口、类增加了泛型支持，从而可以在声明集合变量、创建集合对象向时传入类型实参。例如List<string>、ArrayList<string>。</string></string></p>
<p>当创建带泛型声明的自定义类，为该类定义构造器时，构造器名还是原来的类名，不要增加泛型声明。例如，为Apple<t>类定义构造器，其构造器名依然是Apple,而不是Apple<t>。<code>Java7提供了菱形语法，允许省略&lt;&gt;中的类型实参。</code></t></t></p>
<h1 id="深入泛型"><a href="#深入泛型" class="headerlink" title="深入泛型"></a>深入泛型</h1><h2 id="定义泛型接口、类"><a href="#定义泛型接口、类" class="headerlink" title="定义泛型接口、类"></a>定义泛型接口、类</h2><p>包含泛型声明的类型可以在定义变量、创建对象时传入一个类型实参，从而可以动态地生成无数多个逻辑上的子类，但这种子类在物理上并不存在。</p>
<p>可以为任何类、接口增加泛型声明（并不是只有集合类才可以使用泛型声明，虽然集合类是泛型的重要使用场合）。下面是一个包含泛型声明的类。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Apple</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="comment">//使用T类型形参定义实例变量</span></span><br><span class="line">    <span class="keyword">private</span> T info;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Apple</span><span class="params">()</span> </span>&#123;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//使用T类型形参定义构造器</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Apple</span><span class="params">(T info)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.info = info;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> T <span class="title">getInfo</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> info;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setInfo</span><span class="params">(T info)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.info = info;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Apple&lt;String&gt; a1 = <span class="keyword">new</span> Apple&lt;&gt;(<span class="string">"苹果"</span>);</span><br><span class="line">        System.out.println(a1.getInfo());</span><br><span class="line"></span><br><span class="line">        Apple&lt;Double&gt; a2 = <span class="keyword">new</span> Apple&lt;&gt;(<span class="number">5.67</span>);</span><br><span class="line">        System.out.println(a2.getInfo());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="从泛型类派生子类"><a href="#从泛型类派生子类" class="headerlink" title="从泛型类派生子类"></a>从泛型类派生子类</h2><blockquote>
<p>方法中的形参代表变量、常量、表达式等数据，称为<code>形参</code>，或者称为<code>数据形参</code>。定义方法时可以声明数据形参，调用方法（使用方法）时必须为这些数据形参传入实际的数据；与此类似的是，定义类、接口、方法时可以声明<code>类型形参</code>，使用类、接口、方法时应该为类型形参传入实际的类型。</p>
</blockquote>
<p>当你车床件了带泛型声明的接口、父类之后，可以为该接口创建实现类，或者从该父类派生子类；<code>需要指出的是:</code> <strong>当使用这些接口、父类时不能再包含类型形参。</strong></p>
<p><code>错误写法：</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//定义类A继承Apple类，Apple类不能跟类型形参</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">A</span> <span class="keyword">extends</span> <span class="title">Apple</span>&lt;<span class="title">T</span>&gt; </span>&#123;&#125;</span><br></pre></td></tr></table></figure>

<p><code>正确写法：</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//使用Apple类时，为T形参参入String类型</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">A</span> <span class="keyword">extends</span> <span class="title">Apple</span>&lt;<span class="title">String</span>&gt; </span>&#123;&#125;</span><br></pre></td></tr></table></figure>

<p>调用方法时必须为所有的数据形参传入参数值，与调用方法不同的是，使用类、接口时也可以不为类型参数传入实际的类型参数。如以下代码：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//传入Apple类时，没有为T形参传入实际的类型参数</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">A</span> <span class="keyword">extends</span> <span class="title">Apple</span></span></span><br></pre></td></tr></table></figure>

<p>如果从Apple<string>类派生子类，则在Apple类中所有使用T类型形参的地方都被替换成String类型，即它的子类将会继续继承到String getInfo() 和 void setInfo(String info)两个方法，如果子类需要重写父类的方法，就必须注意这一点。</string></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">A1</span> <span class="keyword">extends</span> <span class="title">Apple</span>&lt;<span class="title">String</span>&gt; </span>&#123;</span><br><span class="line">    <span class="comment">//正确重写了父类的方法，返回值</span></span><br><span class="line">    <span class="comment">//与父类Apple&lt;String&gt;的返回值完全相同</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getInfo</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"子类"</span> + <span class="keyword">super</span>.getInfo();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//下面的方法是错误的，重写父类方法时返回值类型不一致</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">getInfo</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"子类"</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="并不存在泛型类"><a href="#并不存在泛型类" class="headerlink" title="并不存在泛型类"></a>并不存在泛型类</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line">List&lt;String&gt; l1 = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">List&lt;Integer&gt; L2 = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"></span><br><span class="line"><span class="comment">//比较l1和l2的类是否相等，结果为相等</span></span><br><span class="line">System.out.println(l1.getClass() == l2.getClass());</span><br></pre></td></tr></table></figure>

<p>从上面的例子可以看出，不管泛型的世界类型参数是什么，它们在运行时总有同样的类（class）。</p>
<blockquote>
<p>不管为泛型的类型参数传入哪一种类型实参，对于java来说，它们依然别当成同一个类处理，在内存中只占用一块内存空间，因此在静态方法、静态初始化或者静态变量的声明和初始化中不允许使用类型形参。</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">R</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="comment">//下面代码错误，不能再静态变量声明中使用类型形参</span></span><br><span class="line">    <span class="keyword">static</span> T info;</span><br><span class="line">    T age;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">foo</span><span class="params">(T msg)</span> </span>&#123;&#125;</span><br><span class="line">    <span class="comment">//下面代码错误，不能再静态方法中声明中使用类型形参</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">bar</span><span class="params">(T msg)</span> </span>&#123;&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="类型通配符"><a href="#类型通配符" class="headerlink" title="类型通配符"></a>类型通配符</h1><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">(List c)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(init i=<span class="number">0</span>; i &lt; c.size(); i++)&#123;</span><br><span class="line">        System.out.print(c.get(i));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面的例子中，使用List接口没有传入实际的类型参数，会引起泛型警告。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">(List&lt;Object&gt; c)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span>(init i=<span class="number">0</span>; i &lt; c.size(); i++)&#123;</span><br><span class="line">        System.out.print(c.get(i));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面的例子中，表面上看方法声明没有问题，但是传入实际参数值时可能不是我们所期望的，例如以下调用：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">List&lt;String&gt; strList = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"><span class="comment">//将strList作为参数来调用前面的test方法</span></span><br><span class="line">test(strList)</span><br></pre></td></tr></table></figure>

<p>编译时会发生以下错误：<br>无法将Test中的test（java.util.List&lt;java.lang.Object&gt;）应用于(java.util.List<object>)</object></p>
<p>这表明List<string>对象不能被当成List<object>对象使用，也就是说，List<string>类并不是List<object>类的子类。</object></string></object></string></p>
<p><strong>注意：</strong><br>如果Foo是Bar的子类型（子类或者子接口），而G是具有泛型声明的类或接口，G<foo>并不是G<bar>的子类型。这一点非常值得注意，因为它与大部分人的习惯认为是不同的。</bar></foo></p>
<p>数组和泛型有所不同，假设Foo是Bar的一个子类型（子类或者接口），那么Foo[]依然是Bar[]的子类型。但G<foo>不是G<bar>的子类型。</bar></foo></p>
<h2 id="使用类型通配符"><a href="#使用类型通配符" class="headerlink" title="使用类型通配符"></a>使用类型通配符</h2><p>使用类型通配符可以解决上面的问题。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">(List&lt;?&gt; c)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">int</span> i=<span class="number">0</span>;i &lt; c.size(); i++) &#123;</span><br><span class="line">        System.out.println(c.get(i));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这个问号(?)被称为通配符，它的元素类型 可以匹配任何类型。<br>但是这种带通配符的List紧表示它是各种泛型List的父类，并不能把元素加入到其中。因为程序无法确定c集合中元素的类型，所以不能向其中添加对象。</p>
<h3 id="设定通配符的上限"><a href="#设定通配符的上限" class="headerlink" title="设定通配符的上限"></a>设定通配符的上限</h3><p>考虑一个问题，假如有三个形状类，其中Shape是一个抽象父类，该抽象父类有两个子类：Circle和Rectangle。接下来定义一个Canvas类，该画布类可以画数量不定的形状（Shape子类的对象），那应该如何定义这个Canvas类呢？</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">Shape</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">draw</span><span class="params">(Canvas c)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Circle</span> <span class="keyword">extends</span> <span class="title">Shape</span> </span>&#123;</span><br><span class="line">    <span class="comment">//画圆</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">draw</span><span class="params">(Canvas c)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"在画布"</span> + c + <span class="string">"上画一个圆"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Rectangle</span> <span class="keyword">extends</span> <span class="title">Shape</span> </span>&#123;</span><br><span class="line">    <span class="comment">//画矩形</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">draw</span><span class="params">(Canvas c)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"在画布"</span> + c + <span class="string">"上画一个矩形"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Canvas实现：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Canvas</span></span>&#123;</span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">draw</span><span class="params">(List&lt;? extends Shape&gt; shapes)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span>(Shape s:shapes) &#123;</span><br><span class="line">            s.draw(<span class="keyword">this</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="设定类型形参的上限"><a href="#设定类型形参的上限" class="headerlink" title="设定类型形参的上限"></a>设定类型形参的上限</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Apple</span>&lt;<span class="title">T</span> <span class="keyword">extends</span> <span class="title">Number</span>&gt; </span>&#123;</span><br><span class="line">    T col;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Apple&lt;Integer&gt; ai =  <span class="keyword">new</span> Apple&lt;&gt;();</span><br><span class="line">        Apple&lt;Double&gt; ad = <span class="keyword">new</span> Apple&lt;&gt;();</span><br><span class="line">        <span class="comment">//下面代码将会引起编译异常，下面代码试图把String类型传给T形参</span></span><br><span class="line">        <span class="comment">//但String不是Number的子类型，所以引起变异错误。</span></span><br><span class="line">        Apple&lt;String&gt; as = <span class="keyword">new</span> Apple&lt;&gt;();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>多个上限</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Apple</span>&lt;<span class="title">T</span> <span class="keyword">extends</span> <span class="title">Number</span> &amp; <span class="title">java</span>.<span class="title">io</span>.<span class="title">Serializable</span>&gt; </span>&#123;&#125;</span><br></pre></td></tr></table></figure>

<h1 id="泛型方法"><a href="#泛型方法" class="headerlink" title="泛型方法"></a>泛型方法</h1><h2 id="定义泛型方法"><a href="#定义泛型方法" class="headerlink" title="定义泛型方法"></a>定义泛型方法</h2><p>假设有这样一个方法：该方法负责将一个Object数组的所有元素添加到一个Collection集合中。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">fromArrayToCollection</span><span class="params">(Object[] a, Collection&lt;Object&gt; c)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(Object o : a) &#123;</span><br><span class="line">        c.add(o);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面方法中的形参c的数据类型是Collection<object>，假如想放入Object[] a中的内容是String类型的，Collection<string>不是Collection<object>的子类型，所以这个方法很有限。</object></string></object></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line">String[] strArr = &#123;<span class="string">"a"</span>,<span class="string">"b"</span>&#125;;</span><br><span class="line">List&lt;String&gt; strList = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"><span class="comment">//Collection&lt;String&gt;不是Collection&lt;Object&gt;的子类型</span></span><br><span class="line"><span class="comment">//编译错误</span></span><br><span class="line">formArrayToCollection(strArr,strList);</span><br></pre></td></tr></table></figure>

<p>泛型方法：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">static</span> &lt;T&gt; <span class="function"><span class="keyword">void</span> <span class="title">formArrayToCollection</span><span class="params">(T[] a, Collection&lt;T&gt; c)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(T o : a) &#123;</span><br><span class="line">        c.add(o);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="泛型方法和类型通配符的区别"><a href="#泛型方法和类型通配符的区别" class="headerlink" title="泛型方法和类型通配符的区别:"></a>泛型方法和类型通配符的区别:</h2><p>大多数时候都可以使用泛型方法来代替类型通配符。</p>
<p><strong>通配符形式</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Collection</span>&lt;<span class="title">E</span>&gt; </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">boolean</span> <span class="title">containsAll</span><span class="params">(Collection&lt;?&gt; c)</span></span>;</span><br><span class="line">    <span class="function">boolen <span class="title">addAll</span><span class="params">(Collection&lt;? extends E&gt; c)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>泛型方法形式</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Collection</span>&lt;<span class="title">E</span>&gt; </span>&#123;</span><br><span class="line">    &lt;T&gt; <span class="function">boolearn <span class="title">containsAll</span><span class="params">(Collection&lt;T&gt; c)</span></span>;</span><br><span class="line">    &lt;T extends E&gt; <span class="function"><span class="keyword">boolean</span> <span class="title">addAll</span><span class="params">(Collection&lt;T&gt; c)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面的方法中使用了<t extends e>泛型形式，这是定义类型形参时设定上限（其中E是Collection接口里定义的类型形参，在<code>接口里E可以当成普通类型使用</code>）。</t></p>
<p>上面两个方法中类型形参T只使用了一次，<code>类型形参T产生的唯一效果是可以在不同的调用点传入不同的实际类型</code>。对于这种情况，应该使用通配符：<code>通配符就是被设计用来支持灵活的子类化的。</code></p>
<p>泛型方法允许类型形参被用来表示方法的一个或多个参数之间的类型依赖关系，或者方法返回值与参数之间的类型依赖关系。如果没有这样的依赖关系，就不应该使用泛型方法。</p>
<p><strong>同时使用泛型方法和通配符：</strong><br>如java的<code>Collections.copy()</code>方法</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Collections</span></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; <span class="function"><span class="keyword">void</span> <span class="title">copy</span><span class="params">(List&lt;T&gt; dest, List&lt;? extends T&gt; src)</span> </span>&#123;...&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面的copy方法中的dest和src存在明显的依赖关系，从源List复制出来的元素，必须可以“丢进”目标List中，所以源List集合元素的类型只能是目标集合元素的类型的子类型或者它本身。但JDK定义src形参类型时使用的是类型通配符，而不是泛型方法。这是因为：该方法无须向src集合添加元素，也无须修改src集合里的元素，所以可以使用类型通配符，无须使用泛型方法。</p>
<p>如果改写为泛型方法如下：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Collections</span></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;T , S extends T&gt; copy&lt;List&lt;T&gt; dest, List&lt;S&gt; src&gt; &#123;...&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这份泛型方法可以替代前面的方法，但是注意上面的类型形参S,它仅使用了一次，其他参数的类型、方法返回值的类型都不依赖于它，那类型形参S就没有存在的必要，即可以使用通配符来代替S。使用通配符比使用泛型方法（在方法签名中显式声明类型形参）更加清晰。</p>
<p>类型通配符与泛型方法（在方法签名中显式声明类型形参）还有一个<code>显著的区别</code>：类型通配符既可以在方法签名中定义类型形参的类型，也可以用于定义变量的类型；但泛型方法中的类型形参必须在对应方法中显式声明。</p>
<h2 id="Java7的“菱形”语法与泛型构造器"><a href="#Java7的“菱形”语法与泛型构造器" class="headerlink" title="Java7的“菱形”语法与泛型构造器"></a>Java7的“菱形”语法与泛型构造器</h2><p>就像泛型方法允许在方法中声明类型形参一样，Java也允许在构造签名中声明类型形参，这样就产生了所谓的<code>泛型构造器</code>。</p>
<p>作用：在调用构造器时，不仅可以让Java根据数据参数的类型来“推断”类型形参的类型，而且程序员也可以显式地为构造器中的类型形参指定实际的类型。</p>
<p>例子：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span> </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> &lt;T&gt; Foo(T t) &#123;</span><br><span class="line">        System.out.println(t);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">GenericConstructor</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//泛型构造器中的T参数为String</span></span><br><span class="line">        <span class="keyword">new</span> Foo(<span class="string">"疯狂Java讲义"</span>);</span><br><span class="line">        <span class="comment">//泛型构造器中的T参数为Integer</span></span><br><span class="line">        <span class="keyword">new</span> Foo(<span class="number">200</span>);</span><br><span class="line">        <span class="comment">//显式指定泛型构造器的T参数为String</span></span><br><span class="line">        <span class="comment">//传给Foo构造器的实参也是String对象，完全正确</span></span><br><span class="line">        <span class="keyword">new</span> &lt;String&gt; Foo(<span class="string">"疯狂Java讲义"</span>);</span><br><span class="line">        <span class="comment">//显式指定泛型构造器中的T参数为String</span></span><br><span class="line">        <span class="comment">//但传给Foo构造器的实参是Double对象,下面代码错误</span></span><br><span class="line">        <span class="keyword">new</span> &lt;String&gt; Foo(<span class="number">12.3</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>菱形语法：</code>允许调用构造器后使用一对尖括号来代表泛型信息。但是如果程序显式指定了泛型构造器中声明的类型形参的实际类型，则不可以使用“菱形”语法。</p>
<p>如下：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyClass</span>&lt;<span class="title">E</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> &lt;T&gt; MyClass(T t) &#123;</span><br><span class="line">        System.out.println(<span class="string">"t 参数的值为："</span> + t);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">GenericDiamondTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(Stirng[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//MyClass 声明中的E形参是String类型</span></span><br><span class="line">        <span class="comment">//泛型构造器中声明的T形参是Integer类型</span></span><br><span class="line">        MyClass&lt;String&gt; mc1 = <span class="keyword">new</span> MyClass&lt;&gt;(<span class="number">5</span>);</span><br><span class="line">        <span class="comment">//显式指定泛型构造器申明的T是Integer类型</span></span><br><span class="line">        MyClass&lt;String&gt; mc2 = <span class="keyword">new</span> &lt;Integer&gt; MyClass&lt;String&gt;(<span class="number">5</span>);</span><br><span class="line">        <span class="comment">//MyClass 类声明中的E形参是String类型</span></span><br><span class="line">        <span class="comment">//如果显式指定泛型构造器中声明的T形参是Integer类型</span></span><br><span class="line">        <span class="comment">//此时就不能使用“菱形语法”，下面代码是错误的</span></span><br><span class="line">        MyClass&lt;String&gt; mc3 = <span class="keyword">new</span> &lt;Integer&gt; MyClass&lt;&gt;(<span class="number">5</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="设定通配符下限"><a href="#设定通配符下限" class="headerlink" title="设定通配符下限"></a>设定通配符下限</h2><p>假设自己实现一个工具方法：实现将src集合里的元素复制到dest集合里的功能，并返回最后一个被复制的元素，因为dest集合里的所有元素，所以dest集合元素的类型应该是src集合元素类型的父类。为了表示两个参数之间的类型依赖，考虑同时使用通配符、泛型参数来实现该方法。<br>如下：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;T&gt; <span class="function"><span class="keyword">void</span> <span class="title">copy</span><span class="params">(Collections&lt;T&gt; dest,Collection&lt;? extends T&gt; src)</span> </span>&#123;</span><br><span class="line">    T last = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">for</span> (T ele : src) &#123;</span><br><span class="line">        last = ele;</span><br><span class="line">        dest.add(ele);</span><br><span class="line">    &#125;   </span><br><span class="line">    <span class="keyword">return</span> last;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面的方法看起来没有问题，实际上有一个问题：当遍历src集合的元素时，src元素类型是不确定的（只可以坑定它是T的子类），程序只能用T来笼统地表示各宗src集合 的元素类型。例如如下代码：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">List&lt;Number&gt; ln = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">List&lt;Integer&gt; li = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line"><span class="comment">//下面的代码引起编译错误</span></span><br><span class="line">Integer last = copy(ln,li);</span><br></pre></td></tr></table></figure>

<p>上面代码中ln的菱形是List<number>,与copy()方法签名的形参类型进行对比即得到T的实际类型是Number，而不是Integer类型—即返回值类型也是Number类型。实际上返回的值一定是Integer类型的，也就是说在<code>复制集合元素的过程中，丢失了src元素的类型</code>。</number></p>
<p>对于上面的copy()方法，可以这样理解两个集合参数之间的依赖关系：不管集合元素的类型是什么，只要dest集合元素的类型与前者相同或是前者的父类即可。为了表达这种关系，Java允许设定通配符的下限：<code>&lt;? super Type&gt;,这个通配符表示它必须是Type本身，或是Type的父类</code>。</p>
<p>例子：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyUtils</span> </span>&#123;</span><br><span class="line">    <span class="comment">//</span></span><br><span class="line">    <span class="keyword">public</span> staic &lt;T&gt; <span class="function">T <span class="title">copy</span><span class="params">(Collection&lt;? <span class="keyword">super</span> T&gt; dest, Collection&lt;T&gt; src)</span> </span>&#123;</span><br><span class="line">        T last = <span class="keyword">null</span>;</span><br><span class="line">        For(T ele: src) &#123;</span><br><span class="line">            last = ele;</span><br><span class="line">            dest.add(ele);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> last;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        List&lt;Number&gt; ln = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">        List&lt;Integer&gt; li = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">        li.add(<span class="number">5</span>);</span><br><span class="line">        <span class="comment">//此处可准地知道最后一个被复制的元素时Integer类型</span></span><br><span class="line">        <span class="comment">//与src集合元素的类型相同</span></span><br><span class="line">        Integer last = copy(ln,li);  <span class="comment">//①</span></span><br><span class="line">        Systme.out.println(ln);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>使用这种语句，就可以保证程序的①处调用的后推断出最后一个被复制的元素类型是Integer,而不是笼统的Number类型。</p>
<p>JDK中TreeSet<e>有一个构造器也用到了这种设定通配符下限的语法，如下：</e></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">TreeSet(Comparator&lt;? <span class="keyword">super</span> E&gt; c)</span><br></pre></td></tr></table></figure>

<p>TreeSet会对集合中的元素按自然顺序或定制顺序进行排序。如果需要TreeSet对集合中的所有元素进行定制排序，则要求TreeSet对象有一个与之关联的Comparator对象。上面构造器中的参数c就是定制排序的Comparator对象。<br><code>Comparator</code>接口也是一个带泛型声明的接口：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Comparator</span>&lt;<span class="title">T</span>&gt; </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">int</span> <span class="title">compare</span><span class="params">(T fst, T snd)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过这种带下限的通配符的语法，可以创建TreeSet对象时灵活地选择合适的<code>Comparator</code>。假定需要创建一个TreeSet<string>集合，并传入一个可以比较String大小的Comparator，这个Comparator既可以是Comparator<string>，也可以是Comparator<object>—只要尖括号里传入的类型是String的父类型（或它本身）即可。<br>例子：</object></string></string></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TreeSetTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        <span class="comment">//Comparator的实际类型是TreeSet的元素类型的父类，满足要求</span></span><br><span class="line">        TreeSet&lt;String&gt; ts1 = <span class="keyword">new</span> TreeSet&lt;&gt;(<span class="keyword">new</span> Comparator&lt;Object&gt;() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">compare</span><span class="params">(Object fst, Object snd)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">return</span> hashCode() &gt; snd.hashCode() ? <span class="number">1</span> : hashCode() &lt; snd.hashCode() ? -<span class="number">1</span> : <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        ts1.add(<span class="string">"hello"</span>);</span><br><span class="line">        ts1.add(<span class="string">"wa"</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//Comparator的实际类型是TreeSet的元素的类型，满足要求</span></span><br><span class="line">        TreeSet&lt;String&gt; ts2 = <span class="keyword">new</span> TreeSet&lt;&gt;(<span class="keyword">new</span> Comparator&lt;String&gt;() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">compare</span><span class="params">(String first, String second)</span> </span>&#123;</span><br><span class="line">                <span class="keyword">return</span> first.length() &gt; second.length() ? -<span class="number">1</span>:first.length() &lt; second.length() ? <span class="number">1</span> : <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        ts2.add(<span class="string">"hello"</span>);</span><br><span class="line">        ts2.add(<span class="string">"wa"</span>);</span><br><span class="line">        System.out.println(ts1);</span><br><span class="line">        System.out.println(ts2);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过这种通配符下限的方式来定义TreeSet构造器的参数，就可以将所有可用的Comparator作为参数传入，从而增加了程序的灵活性。TreeMap也有类似的用法。</p>
<h1 id="Java8改进的类型推断"><a href="#Java8改进的类型推断" class="headerlink" title="Java8改进的类型推断"></a>Java8改进的类型推断</h1><p>两个方面：</p>
<ul>
<li>可通过调用方法的上下文来推断类型参数的目标类型。</li>
<li>可在方法调用链4中，将推断得到的类型参数传递到最后一个方法。</li>
</ul>
<p>例子：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyUtil</span>&lt;<span class="title">E</span>&gt; </span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;Z&gt; <span class="function">MyUtil <span class="title">nil</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> &lt;Z&gt; <span class="function">myUtil&lt;Z&gt; <span class="title">cons</span><span class="params">(Z head, MyUtil&lt;Z&gt; tail)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function">E <span class="title">head</span><span class="params">()</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">InferenceTest</span> </span>&#123;</span><br><span class="line">    <span class="comment">//可以方法赋值的目标参数来推断类型参数为String</span></span><br><span class="line">    MyUtil&lt;String&gt; ls = MyUtil.nil(); <span class="comment">//①</span></span><br><span class="line">    <span class="comment">//无须使用下面语句在调用nil()方法时指定类型参数的类型</span></span><br><span class="line">    MyUtil&lt;String&gt; mu = MyUtil.&lt;String&gt;nil();   <span class="comment">//②</span></span><br><span class="line">    <span class="comment">//可调用cons()方法所需的参数类型来推断类型参数为Integer</span></span><br><span class="line">    MyUtil.cons(<span class="number">42</span>, MyUtil.nil());  <span class="comment">//③</span></span><br><span class="line">    <span class="comment">//无须使用下面的语句在调用nil()方法是指定类型参数的类型</span></span><br><span class="line">    MyUtil.cons(<span class="number">42</span>, MyUtil.&lt;Integer&gt;nil()); <span class="comment">//④</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>①②代码的作用完全相同。。。</p>
<h1 id="擦除和转换"><a href="#擦除和转换" class="headerlink" title="擦除和转换"></a>擦除和转换</h1><p>在严格的泛型代码里，带泛型声明的类总应该带着类型参数。但为了与老的Java代码保持一致，也允许在使用带泛型声明的类时不指定实际的类型参数。如果没有这个泛型类指定实际的类型参数。如果没有为这个泛型类型指定实际的类型参数，则该类型参数被称作<code>raw type</code>，默认是声明该类型参数时指定的第一个上限类型。</p>
<p>当把一个具有泛型类型信息的对象赋给一个没有泛型信息的变量时，<code>所有在尖括号之间的类型信息都将被扔掉</code>。比如一个List<string>类型转换为List，则该List对集合元素的类型检查<code>变成了类型参数的上限（Object）</code>。</string></p>
<p>例子：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Apple</span>&lt;<span class="title">T</span> <span class="keyword">extends</span> <span class="title">Number</span>&gt; </span>&#123;</span><br><span class="line">    T size;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Apple</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Apple</span><span class="params">(T size)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.size = size;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setSize</span><span class="params">(T size)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.size = size;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getSize</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.size;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ErasureTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">mian</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Apple&lt;Integer&gt; a = <span class="keyword">new</span> Apple&lt;&gt;(<span class="number">6</span>);  <span class="comment">//①</span></span><br><span class="line">        <span class="comment">//a 的 getSize() 方法返回Integer对象</span></span><br><span class="line">        Integer as = a.getSize();</span><br><span class="line">        <span class="comment">//把a对象赋给Apple变量，丢失尖括号里的类型信息</span></span><br><span class="line">        Apple b = a;    <span class="comment">//②</span></span><br><span class="line">        <span class="comment">//b只知道size的类型是Number</span></span><br><span class="line">        Number size1 = b.getSize();</span><br><span class="line">        <span class="comment">//下面代码引起编译错误</span></span><br><span class="line">        Integer size2 = b.getSize();    <span class="comment">//③</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>程序在①处创建了一个Apple对象，该Apple对象传入了Integer作为类型形参的值，所以调用a的getSize()方法时返回Integer类型的值。在②处，把a赋值给一个不带泛型信息的b变量时，编译器就会丢失a对象的泛型信息，即所有尖括号里的信息都会丢失–因为Apple的类型形参的上限是Number类，所以编译器依然知道b的getSize()方法返回Number类型，但具体是哪个子类就不清楚了。</p>
<p>从逻辑上看，List<string>是List的子类，如果直接把一个List对象赋给一个List<string> 对象应该引起编译错误，但实际上不会。对泛型而言，可以直接把一个List对象赋给一个List<string>对象，编译器仅仅提示“未经检查的转换”。</string></string></string></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ErasureTest2</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        List&lt;Integer&gt; li = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">        li.add(<span class="number">6</span>);</span><br><span class="line">        li.add(<span class="number">9</span>);</span><br><span class="line">        List list = li; <span class="comment">//丢失类型信息</span></span><br><span class="line">        <span class="comment">//下面代码引起“未经检查的转换”警告，编译、运行时完全正常</span></span><br><span class="line">        List&lt;String&gt; ls = list; <span class="comment">//①</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//但只要访问ls里的元素，如下面代码将引起运行时异常</span></span><br><span class="line">        System.out.println(ls.get(<span class="number">0</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面程序中定义了一个List<ingeger>对象，这个List对象保留了集合元素的类型信息。当把这个List对象赋给一个List类型的list后，编译器就会丢失前者的泛型信息，即丢失list集合元素的类型信息，这是<code>典型的擦除</code>。Java又允许直接把List对象赋给一个List<type>(Type 可以是任何类型)类型的变量，所以程序在 ①处可以编译通过，只是发出“未经检查的转换”警告。但对list变量实际上引用的是List<integer>集合，所以当试图把该集合里的元素当成String类型的对象取出时，将引发运行时异常。</integer></type></ingeger></p>
<p>下面代码与上面代码的行为完全类似。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ErasureTest3</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        List li = <span class="keyword">new</span> ArrayList();</span><br><span class="line">        li.add(<span class="number">6</span>);</span><br><span class="line">        li.add(<span class="number">9</span>);</span><br><span class="line">        System.out.println((Stirng)li.get(<span class="number">0</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>程序从li中获取一个元素，并试图强制类型转换为String类型，将一引起运行时异常。</p>
<h1 id="泛型与数组"><a href="#泛型与数组" class="headerlink" title="泛型与数组"></a>泛型与数组</h1><p>Java泛型有一个很重要的设计原则–如果一段代码在编译时没有提出“[unchecked]未经检查的转换”警告，则程序在运行时不会引发ClassCastException异常。正是基于这个原因，<code>所以数组元素的类型不能包含类型变量或类型形参，除非是无上限的类型通配符。但可以声明元素类型包含类型变量或类型形参的数组。</code>也就是说，只能声明List<string>[]形式的数组，但不能创建ArrayList<string>[10]这样的数组对象。</string></string></p>
<p>例如：加入Java支持创建ArrayList<string>[10]这样的数组对象，则有如下程序：</string></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//下面代码编译时有“[unchecked]未经检查的转换”警告</span></span><br><span class="line">List&lt;String&gt; lsa = <span class="keyword">new</span> ArrayList&lt;String&gt;[<span class="number">10</span>];    <span class="comment">//①</span></span><br><span class="line"><span class="comment">//将lsa向上转型为Object[]lsa;</span></span><br><span class="line">Object[] oa = (Object[])lsa;</span><br><span class="line">List&lt;Integer&gt; li = <span class="keyword">new</span> ArrayList&lt;Integer&gt;();</span><br><span class="line">li.add(<span class="keyword">new</span> Integer(<span class="number">3</span>));</span><br><span class="line"><span class="comment">//将List&lt;Integer&gt;对象作为oa的第二个元素</span></span><br><span class="line"><span class="comment">//下面代码没有任何警告</span></span><br><span class="line">oa[<span class="number">1</span>] = li;</span><br><span class="line"><span class="comment">//下面代码也不会有任何警告，但将引发ClassCastException异常</span></span><br><span class="line">String s = lsa[<span class="number">1</span>].get(<span class="number">0</span>);   <span class="comment">//②</span></span><br></pre></td></tr></table></figure>

<p>①处的代码声明了List<string>[]类型的数组变量，这是允许的；但不允许创建List<string>[]类型的对象，所以创建了一个类型为ArrayList[10]的数组对象，这是允许的。只是把ArrayList[10]对象赋给 List<string>[]变量时会有编译警告“[unchecked]未经检查的转换”，即编译器并不保证这段代码是类型安全的。上面的代码同样会在②处引起运行时异常，但因为编译器已经提出了警告，所以完全可能出现这种异常。</string></string></string></p>
<p><code>Java允许创建无上限的通配符泛型数组</code>,例如new ArrayList&lt;?&gt;[10]，因此也可以将第一段代码该为使用无上限的通配符泛型数组，在这种情况下，程序不得不进行强制类型转换。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">List&lt;?&gt;[] lsa = <span class="keyword">new</span> ArrayList&lt;?&gt;[<span class="number">10</span>];</span><br><span class="line">Object[] oa = lsa;</span><br><span class="line">List&lt;Ingeger&gt; li = <span class="keyword">new</span> ArrayList&lt;Integer&gt;();</span><br><span class="line">li.add(<span class="keyword">new</span> Integer(<span class="number">3</span>));</span><br><span class="line">oa[<span class="number">1</span>] = li;</span><br><span class="line"><span class="comment">//下面的代码引发ClassCastException异常</span></span><br><span class="line">String s = (String)lsa[<span class="number">1</span>].get(<span class="number">0</span>);</span><br></pre></td></tr></table></figure>

<p>上面的代码会引起ClassCastException，因为程序需要将ls的第一个数组元素的第一个集合元素强制类型转换为String类型，所以程序应该自己通过instanceof运算符来保证它的数据类型。<br>如：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">List&lt;?&gt;[] lsa = <span class="keyword">new</span> ArrayList&lt;?&gt;[<span class="number">10</span>];</span><br><span class="line">Object[]  oa = lsa;</span><br><span class="line">List&lt;Integer&gt; li = <span class="keyword">new</span> ArrayList&lt;Integer&gt;();</span><br><span class="line">oa[<span class="number">1</span>] = li;</span><br><span class="line">Object target = lsa[<span class="number">1</span>].get(<span class="number">0</span>);</span><br><span class="line"><span class="keyword">if</span>(target <span class="keyword">instanceof</span> String) &#123;</span><br><span class="line">    <span class="comment">//下面代码安全了</span></span><br><span class="line">    String s = (String) target;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>与此类似的是，创建元素类型是类型变量的数组对象也将导致编译错误。<br>如：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">&lt;T&gt; T[] makeArray(Collection&lt;T&gt; coll) &#123;</span><br><span class="line">    <span class="comment">//导致编译错误</span></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> T[coll.size()];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>由于类型变量在运行时并不存在，而编译器无法确定实际类型是什么，因此会导致编译错误。</p>

        </div>

        <blockquote class="post-copyright">
    <div class="content">
        
<span class="post-time">
    最后更新时间：<time datetime="2020-03-18T04:04:03.813Z" itemprop="dateUpdated">2020年3月18日 12:04</time>
</span><br>


        这里写留言或版权声明：<a href="/blog/2017/02/11/java/泛型一/" target="_blank" rel="external">http://windynature.oschina.io/2017/02/11/java/泛型一/</a>
    </div>
    <footer>
        <a href="http://windynature.oschina.io">
            <img src="/blog/img/user2.jpg" alt="windy">
            windy
        </a>
    </footer>
</blockquote>

        


        <div class="post-footer">
            
	<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/blog/tags/泛型/">泛型</a></li></ul>


            
<div class="page-share-wrap">
    

<div class="page-share" id="pageShare">
    <ul class="reset share-icons">
      <li>
        <a class="weibo share-sns" target="_blank" href="http://service.weibo.com/share/share.php?url=http://windynature.oschina.io/2017/02/11/java/泛型一/&title=《泛型初解》 — 奔跑的蜗牛&pic=http://windynature.oschina.io/img/user2.jpg" data-title="微博">
          <i class="icon icon-weibo"></i>
        </a>
      </li>
      <li>
        <a class="weixin share-sns wxFab" href="javascript:;" data-title="微信">
          <i class="icon icon-weixin"></i>
        </a>
      </li>
      <li>
        <a class="qq share-sns" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=http://windynature.oschina.io/2017/02/11/java/泛型一/&title=《泛型初解》 — 奔跑的蜗牛&source=" data-title=" QQ">
          <i class="icon icon-qq"></i>
        </a>
      </li>
      <li>
        <a class="facebook share-sns" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=http://windynature.oschina.io/2017/02/11/java/泛型一/" data-title=" Facebook">
          <i class="icon icon-facebook"></i>
        </a>
      </li>
      <li>
        <a class="twitter share-sns" target="_blank" href="https://twitter.com/intent/tweet?text=《泛型初解》 — 奔跑的蜗牛&url=http://windynature.oschina.io/2017/02/11/java/泛型一/&via=http://windynature.oschina.io" data-title=" Twitter">
          <i class="icon icon-twitter"></i>
        </a>
      </li>
      <li>
        <a class="google share-sns" target="_blank" href="https://plus.google.com/share?url=http://windynature.oschina.io/2017/02/11/java/泛型一/" data-title=" Google+">
          <i class="icon icon-google-plus"></i>
        </a>
      </li>
    </ul>
 </div>



    <a href="javascript:;" id="shareFab" class="page-share-fab waves-effect waves-circle">
        <i class="icon icon-share-alt icon-lg"></i>
    </a>
</div>



        </div>
    </div>

    
<nav class="post-nav flex-row flex-justify-between">
  
    <div class="waves-block waves-effect prev">
      <a href="/blog/2017/02/12/java/proxy/" id="post-prev" class="post-nav-link">
        <div class="tips"><i class="icon icon-angle-left icon-lg icon-pr"></i> Prev</div>
        <h4 class="title">代理模式</h4>
      </a>
    </div>
  

  
    <div class="waves-block waves-effect next">
      <a href="/blog/2017/01/26/工具/git远程操作详解/" id="post-next" class="post-nav-link">
        <div class="tips">Next <i class="icon icon-angle-right icon-lg icon-pl"></i></div>
        <h4 class="title">Git远程操作详解</h4>
      </a>
    </div>
  
</nav>



    

<div class="comments" id="comments">
    <div class="ds-thread" data-thread-key="java/泛型一" data-title="泛型初解" data-url="http://windynature.oschina.io/2017/02/11/java/泛型一/"></div>
</div>
<script>
lazyScripts.push('//cdn.bootcss.com/marked/0.3.6/marked.min.js');

var duoshuoQuery = {short_name:'ysblog', theme: 'none'};
lazyScripts.push('//unpkg.com/hexo-theme-material-indigo@1.5.2/js/embed.min.js');


</script>










</article>



</div>

        <footer class="footer">
    <div class="top">
        
<p>
    <span id="busuanzi_container_site_uv" style='display:none'>
        站点总访客数：<span id="busuanzi_value_site_uv"></span>
    </span>
    <span id="busuanzi_container_site_pv" style='display:none'>
        站点总访问量：<span id="busuanzi_value_site_pv"></span>
    </span>
</p>


        <p>
            <span><a href="/atom.xml" target="_blank" class="rss" title="rss"><i class="icon icon-lg icon-rss"></i></a></span>
            <span>博客内容遵循 <a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享 署名 - 非商业性 - 相同方式共享 4.0协议</a></span>
        </p>
    </div>
    <div class="bottom">
        <p>
            <span>Power by <a href="http://hexo.io/" target="_blank">Hexo</a> Theme <a href="https://github.com/yscoder/hexo-theme-indigo" target="_blank">indigo</a></span>
            <span>奔跑的蜗牛 &copy; 2015 - 2020</span>
        </p>
    </div>
</footer>

    </main>
    <div class="mask" id="mask"></div>
<a href="javascript:;" id="gotop" class="waves-effect waves-circle waves-light"><span class="icon icon-lg icon-chevron-up"></span></a>



<div class="global-share" id="globalShare">
    <ul class="reset share-icons">
      <li>
        <a class="weibo share-sns" target="_blank" href="http://service.weibo.com/share/share.php?url=http://windynature.oschina.io/2017/02/11/java/泛型一/&title=《泛型初解》 — 奔跑的蜗牛&pic=http://windynature.oschina.io/img/user2.jpg" data-title="微博">
          <i class="icon icon-weibo"></i>
        </a>
      </li>
      <li>
        <a class="weixin share-sns wxFab" href="javascript:;" data-title="微信">
          <i class="icon icon-weixin"></i>
        </a>
      </li>
      <li>
        <a class="qq share-sns" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=http://windynature.oschina.io/2017/02/11/java/泛型一/&title=《泛型初解》 — 奔跑的蜗牛&source=" data-title=" QQ">
          <i class="icon icon-qq"></i>
        </a>
      </li>
      <li>
        <a class="facebook share-sns" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=http://windynature.oschina.io/2017/02/11/java/泛型一/" data-title=" Facebook">
          <i class="icon icon-facebook"></i>
        </a>
      </li>
      <li>
        <a class="twitter share-sns" target="_blank" href="https://twitter.com/intent/tweet?text=《泛型初解》 — 奔跑的蜗牛&url=http://windynature.oschina.io/2017/02/11/java/泛型一/&via=http://windynature.oschina.io" data-title=" Twitter">
          <i class="icon icon-twitter"></i>
        </a>
      </li>
      <li>
        <a class="google share-sns" target="_blank" href="https://plus.google.com/share?url=http://windynature.oschina.io/2017/02/11/java/泛型一/" data-title=" Google+">
          <i class="icon icon-google-plus"></i>
        </a>
      </li>
    </ul>
 </div>


<div class="page-modal wx-share" id="wxShare">
    <a class="close" href="javascript:;"><i class="icon icon-close"></i></a>
    <p>扫一扫，分享到微信</p>
    <img src="" alt="微信分享二维码">
</div>




    <script src="//cdn.bootcss.com/node-waves/0.7.4/waves.min.js"></script>
<script>
var BLOG = { ROOT: '/blog/', SHARE: true, REWARD: false };



</script>

<script src="//unpkg.com/hexo-theme-material-indigo@1.5.2/js/main.min.js"></script>


<div class="search-panel" id="search-panel">
    <ul class="search-result" id="search-result"></ul>
</div>
<template id="search-tpl">
<li class="item">
    <a href="{path}" class="waves-block waves-effect">
        <div class="title ellipsis" title="{title}">{title}</div>
        <div class="flex-row flex-middle">
            <div class="tags ellipsis">
                {tags}
            </div>
            <time class="flex-col time">{date}</time>
        </div>
    </a>
</li>
</template>

<script src="//unpkg.com/hexo-theme-material-indigo@1.5.2/js/search.min.js" async></script>






<script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>





</body>
</html>
